package com.zff.rental.service.apartment;


import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.zff.rental.common.BusinessException;
import com.zff.rental.common.PageVo;
import com.zff.rental.common.ResultCode;
import com.zff.rental.entity.apartment.ApartmentEntity;
import com.zff.rental.entity.apartment.ApartmentManager;
import com.zff.rental.entity.apartment.AttachmentEntity;
import com.zff.rental.mapstruct.apartment.ApartmentMapstruct;
import com.zff.rental.mapstruct.apartment.AttachmentMapstruct;
import com.zff.rental.mapstruct.apartment.RoomMapstruct;
import com.zff.rental.po.apartment.RecommendApartmentPo;
import com.zff.rental.service.BaseService;
import com.zff.rental.tools.UserContextHolder;
import com.zff.rental.utils.WGS84Utils;
import com.zff.rental.utils.collectors.CollectionsUtils;
import com.zff.rental.utils.collectors.ComparatorUtils;
import com.zff.rental.utils.specification.SpecBuilder;
import com.zff.rental.vo.apartment.apartment.*;
import com.zff.rental.vo.apartment.apartment.manager.ApartmentManagerParams;
import com.zff.rental.vo.apartment.apartment.manager.ApartmentManagerVo;
import com.zff.rental.vo.apartment.apartment.manager.AuditApartmentParams;
import com.zff.rental.vo.apartment.attachment.AttachmentVo;
import com.zff.rental.vo.apartment.room.RoomVo;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author HCY
 */
@Service
public class ApartmentServer extends BaseService {

    @Transactional
    public ApartmentVo add(AddApartmentParams params) {
        ApartmentEntity map = ApartmentMapstruct.INSTANCE.map(params);
        if (apartmentRepository.existsByName(map.getName())) {
            throw new BusinessException("公寓名称已存在");
        }
        ApartmentEntity saveResult = apartmentRepository.save(map);
        ApartmentVo vo = ApartmentMapstruct.INSTANCE.map(saveResult);

        Long id = map.getId();
        List<AttachmentEntity> attachmentEntityList = AttachmentMapstruct.INSTANCE.map(params.getAttachmentList(), id, 1);
        if (!attachmentEntityList.isEmpty()) {
            List<AttachmentEntity> attachmentEntities = attachmentRepository.saveAll(attachmentEntityList);
            vo.setAttachmentList(AttachmentMapstruct.INSTANCE.map(attachmentEntities));
        }

        // 设置管理用户
        setApartmentManageUser(id, params.getManagerUserList());
        return vo;
    }

    /**
     * 添加公寓时，设置公寓管理用户
     */
    private void setApartmentManageUser(Long apartmentId, List<ApartmentManagerParams> adminUserList) {
        // 获取操作当前用户
        Long userId = UserContextHolder.getUserId();

        if (adminUserList == null || adminUserList.isEmpty()) {
            apartmentManagerRepository.save(new ApartmentManager(apartmentId, userId, (byte) 1, (byte) 0));
        } else {
            boolean hasAdminUser = false;
            List<ApartmentManager> userList = new ArrayList<>();
            for (ApartmentManagerParams user : adminUserList) {
                if (Objects.equals(user.getIsAdmin(), 1)) {
                    hasAdminUser = true;
                }
                // 配置是管理员或管家，则保存
                if (user.getIsAdmin() == 1 || user.getIsButler() == 1) {
                    userList.add(new ApartmentManager(apartmentId, user.getUserId(), user.getIsAdmin(), user.getIsButler()));
                }
            }
            // 没有配置管理员,则配置当前操作人为管理员
            if (!hasAdminUser) {
                ApartmentManager one = CollUtil.findOne(userList, e -> Objects.equals(e.getUserId(), userId));
                if (one == null) {
                    userList.add(new ApartmentManager(apartmentId, userId, (byte) 1, (byte) 0));
                } else {
                    one.setIsAdmin((byte) 1);
                }
            }
            apartmentManagerRepository.saveAll(userList);
        }
    }

    @Transactional
    public ApartmentVo modify(ModifyApartmentParams params) {
        Long apartmentId = params.getId();
        ApartmentEntity findResult = apartmentRepository.findById(apartmentId).orElseThrow(ResultCode.RECORD_NOT_EXISTS::toException);

        if (findResult == null) {
            throw ResultCode.RECORD_NOT_EXISTS.toException();
        }

        // 此公寓管理用户列表
        List<ApartmentManager> oldManagerList = apartmentManagerRepository.findAllByApartmentId(apartmentId);

        ApartmentMapstruct.INSTANCE.merge(findResult, params);
        ApartmentEntity saveResult = apartmentRepository.save(findResult);
        ApartmentVo vo = ApartmentMapstruct.INSTANCE.map(saveResult);

        // 保存附件
        List<AttachmentEntity> attachmentEntities = attachmentRepository.saveAllOrDelete(params.getAttachmentList(), saveResult.getId(), 1);
        vo.setAttachmentList(AttachmentMapstruct.INSTANCE.map(attachmentEntities));

        // 设置新管理用户列表
        List<ApartmentManagerParams> adminUserList = params.getManagerUserList();
        List<ApartmentManager> newManagerList = adminUserList.stream()
                .map(e -> new ApartmentManager(apartmentId, e.getUserId(), e.getIsAdmin(), e.getIsButler()))
                .collect(Collectors.toList());

        Set<ApartmentManager> removeList = CollectionsUtils.reduceToSet(oldManagerList, newManagerList, ApartmentManager::getUserId);
        if (!removeList.isEmpty()) {
            apartmentManagerRepository.deleteAll(removeList);
        }
        Set<ApartmentManager> updateList = CollectionsUtils.reduceToSet(newManagerList, oldManagerList);
        if (!updateList.isEmpty()) {
            apartmentManagerRepository.saveAll(updateList);
        }

        // 公寓管理用户
        List<ApartmentManager> managerList = apartmentManagerRepository.findAllByApartmentId(apartmentId);
        Set<Long> userIdSet = managerList.stream().map(ApartmentManager::getUserId).collect(Collectors.toSet());
        Map<Long, String> userNameMap = userRepository.findNickname(userIdSet);
        List<ApartmentManagerVo> managerVoList = managerList.stream()
                .map(e -> new ApartmentManagerVo(e, userNameMap.get(e.getUserId()))).toList();
        vo.setManagerUserList(managerVoList);
        return vo;
    }

    @Transactional
    public Boolean remove(Long id) {
        if (roomRepository.existsByApartmentId(id)) {
            throw ResultCode.APARTMENT_HAS_ROOM.toException();
        }

        // 公寓
        apartmentRepository.deleteById(id);

        // 附件
        attachmentRepository.deleteAllByBelongId(id);

        // 公寓管理用户
        apartmentManagerRepository.deleteByApartmentId(id);
        return true;
    }

    public ApartmentVo details(Long id) {
        ApartmentEntity findResult = apartmentRepository.findById(id).orElseThrow(ResultCode.RECORD_NOT_EXISTS::toException);
        ApartmentVo vo = ApartmentMapstruct.INSTANCE.map(findResult);

        // 公寓图片
        List<AttachmentEntity> attachmentEntityList = attachmentRepository.findAllByBelongIdAndBelongType(id, 1);
        attachmentEntityList.sort(Comparator.comparingInt(e -> e.getSerialNumber() == null ? Integer.MAX_VALUE : e.getSerialNumber()));
        List<AttachmentVo> attachmentList = AttachmentMapstruct.INSTANCE.map(attachmentEntityList);
        attachmentList.sort(Comparator.comparingInt(v -> v.getSerialNumber() == null ? Integer.MAX_VALUE : v.getSerialNumber()));
        vo.setAttachmentList(attachmentList);

        // 公寓管理用户
        List<ApartmentManager> managerList = apartmentManagerRepository.findAllByApartmentId(id);
        Set<Long> userIdSet = managerList.stream().map(ApartmentManager::getUserId).collect(Collectors.toSet());
        Map<Long, String> userNameMap = userRepository.findNickname(userIdSet);
        List<ApartmentManagerVo> managerVoList = managerList.stream()
                .map(e -> new ApartmentManagerVo(e, userNameMap.get(e.getUserId()))).toList();
        vo.setManagerUserList(managerVoList);

        return vo;
    }

    public PageVo<ApartmentVo> list(ApartmentListParams params) {
        Specification<ApartmentEntity> where = SpecBuilder.<ApartmentEntity>builder()
                .likeAll("name", params.getName())
                .likeAll("businessArea", params.getBusinessArea())
                .add(params.isIgnore(), params.toSpecification())
                .buildAnd();

        Page<ApartmentEntity> findResult = apartmentRepository.findAll(where, params.generatePageable());

        List<ApartmentEntity> records = findResult.getContent();
        List<ApartmentVo> voList = ApartmentMapstruct.INSTANCE.mapAsList(records);

        return PageVo.of(findResult, voList);
    }

    public PageVo<RecommendApartmentVo> recommend(RecommendApartmentListParams params) {
        IPage<RecommendApartmentPo> findResult = apartmentMapper.recommendList(params, PageDTO.of(params.getPage(), params.getSize()));

        // 以公寓分组
        Map<Long, List<RecommendApartmentPo>> map = findResult.getRecords().stream()
                .collect(Collectors.groupingBy(RecommendApartmentPo::getId, LinkedHashMap::new, Collectors.toList()));

        ArrayList<RecommendApartmentVo> list = new ArrayList<>();
        map.forEach((k, v) -> {
            RecommendApartmentVo vo = ApartmentMapstruct.INSTANCE.mapToRecommendApartment(v.get(0));

            // 计算WGS84距离
            if (params.getUserLatitude() != null && params.getUserLongitude() != null) {
                Double distance = WGS84Utils.getDistance(params.getUserLatitude(), params.getUserLongitude(),
                        vo.getLocationLatitude(), vo.getLocationLongitude());
                if (distance != null) {
                    if (distance < 1000) {
                        vo.setDistance(String.format("%.0f", distance) + "米");
                    } else if (distance < 5000) {
                        vo.setDistance(String.format("%.1f", distance / 1000) + "公里");
                    } else {
                        vo.setDistance(String.format("%.0f", distance / 1000) + "公里");
                    }
                }
            }

            // 转换为公寓房间，并排序
            ArrayList<RoomVo> roomVoList = new ArrayList<>();
            vo.setRoomList(roomVoList);
            v.forEach(e -> {
                if (e.getRoomId() != null) {
                    RoomVo roomVo = RoomMapstruct.INSTANCE.map(e);
                    roomVoList.add(roomVo);
                }
            });
            roomVoList.sort((o1, o2) -> {
                byte discountedRoom1 = Objects.requireNonNullElse(o1.getDiscountedRoom(), (byte) 0);
                byte discountedRoom2 = Objects.requireNonNullElse(o2.getDiscountedRoom(), (byte) 0);
                // 特价房排在前面
                int compare = Byte.compare(discountedRoom2, discountedRoom1);
                if (compare != 0) {
                    return compare;
                } else {
                    return ComparatorUtils.compare(o1.getName(), o2.getName(), true);
                }
            });
            list.add(vo);
        });

        list.sort((o1, o2) -> {
            // 优先以空房数量排序
            List<RoomVo> roomList1 = o1.getRoomList();
            List<RoomVo> roomList2 = o2.getRoomList();
            long size1 = CollectionsUtils.getSizeOrZero(roomList1);
            long size2 = CollectionsUtils.getSizeOrZero(roomList2);
            int compareSize = Long.compare(size2, size1);
            if (compareSize != 0) {
                return compareSize;
            }
            // 比较特价房
            boolean discountedRoom1 = Optional.of(roomList1)
                    .filter(temList -> !temList.isEmpty())
                    .map(temList -> temList.get(0))
                    .map(RoomVo::getDiscountedRoom)
                    .map(d -> Objects.equals(d, (byte) 1)).orElse(false);
            boolean discountedRoom2 = Optional.of(roomList2)
                    .filter(temList -> !temList.isEmpty())
                    .map(temList -> temList.get(0))
                    .map(RoomVo::getDiscountedRoom)
                    .map(d -> Objects.equals(d, (byte) 1)).orElse(false);
            return Boolean.compare(discountedRoom2, discountedRoom1);
        });

        return PageVo.of((int) findResult.getCurrent(), (int) findResult.getSize(), findResult.getTotal(), list);
    }

    public Boolean audit(AuditApartmentParams params) {
        ApartmentEntity apartmentEntity = apartmentMapper.selectById(params.getApartmentId());
        if (apartmentEntity == null) {
            throw ResultCode.RECORD_NOT_EXISTS.toException();
        }
        apartmentMapper.updateAudit(params.getApartmentId(), params.getAuditStatus(), params.getAuditMsg());
        return true;
    }

    public Boolean modifyApartmentSort(List<ModifyApartmentSortParams> params) {
        apartmentMapper.updateBatchSerialNumber(params);
        return true;
    }

    public List<SimpleApartmentVo> simpleList() {
        return apartmentMapper.simpleApartmentList();
    }
}
